import logging

from fastapi import FastAPI, Request
from fastapi.exceptions import RequestValidationError
from pydantic import ValidationError
from pymysql import OperationalError
from starlette.exceptions import HTTPException as StarletteHTTPException
from starlette.responses import JSONResponse

from app.exceptions.base import AppException
from app.http_base import HttpResp

logger = logging.getLogger(__name__)


def configure_exception(app: FastAPI):
    """
    配置全局异常处理
    """

    @app.exception_handler(RequestValidationError)
    async def validation_exception_handler(request: Request, exc: RequestValidationError):
        """
        处理参数验证的异常
        """
        errs = exc.errors()
        resp = HttpResp.PARAMS_VALID_ERROR
        if errs and errs[0].get('type', '').startswith('type_error.'):
            resp = HttpResp.PARAMS_TYPE_ERROR
            logger.warning('validation_exception_handler: url=[%s], errs=[%s]', request.url.path, errs)
        return JSONResponse(status_code=resp.code, content={'code': resp.code, 'message': resp.message, 'data': errs})

    @app.exception_handler(StarletteHTTPException)
    async def http_exception_handler(request: Request, exc: StarletteHTTPException):
        """
        处理客户端请求异常
        """
        logger.warning('http_exception_handler: url=[%s], status_code=[%s]', request.url.path, exc.status_code)
        resp = HttpResp.SYSTEM_ERROR
        if exc.status_code == 404:
            resp = HttpResp.REQUEST_404_ERROR
        if exc.status_code == 405:
            resp = HttpResp.REQUEST_METHOD_ERROR
        return JSONResponse(status_code=resp.code, content={'code': resp.code, 'message': resp.message, 'data': []})

    @app.exception_handler(AssertionError)
    async def assert_exception_handler(request: Request, exc: AssertionError):
        """
        处理断言异常
        """
        resp = HttpResp.ASSERT_ARGUMENT_ERROR
        errs = ','.join(exc.args) if exc.args else resp.message
        logger.warning('app_exception_handler: url=[%s], errs=[%s]', request.url.path, errs)
        return JSONResponse(status_code=resp.code, content={'code': resp.code, 'message': errs, 'data': []})

    @app.exception_handler(AppException)
    async def app_exception_handler(request: Request, exc: AppException):
        """
        处理自定义异常
        """
        if exc.echo_exc:
            logger.error('app_exception_handler: url=[%s]', request.url.path)
            logger.error(exc, exc_info=True)
        return JSONResponse(
            status_code=exc.code,
            content={'code': exc.code, 'message': exc.message, 'data': []})

    @app.exception_handler(ValidationError)
    async def validation_exception_handler(request: Request, exc: ValidationError):
        """
        处理参数验证的异常 (除请求参数验证之外的)
        """
        logger.error('validation_exception_handler: url=[%s]', request.url.path)
        logger.error(exc, exc_info=True)
        return JSONResponse(
            status_code=200,
            content={'code': HttpResp.SYSTEM_ERROR.code, 'message': HttpResp.SYSTEM_ERROR.message, 'data': []})

    @app.exception_handler(OperationalError)
    async def db_opr_error_handler(request: Request, exc: OperationalError):
        """
        处理数据库连接异常
        """
        resp = HttpResp.SYSTEM_ERROR
        logger.error('db_opr_error_handler: url=[%s]', request.url.path)
        logger.error(exc, exc_info=True)
        return JSONResponse(status_code=resp.code, content={'code': resp.code, 'message': resp.message, 'data': []})

    @app.exception_handler(Exception)
    async def global_exception_handler(request: Request, exc: Exception):
        """
        处理服务端异常，全局异常处理
        """
        resp = HttpResp.SYSTEM_ERROR
        logger.error('global_exception_handler: url=[%s]', request.url.path)
        logger.error(exc, exc_info=True)
        return JSONResponse(status_code=resp.code, content={'code': resp.code, 'message': resp.message, 'data': []})
